# -*- coding: UTF-8 -*-

import json
from Structs import *
import uuid


class FstBuilder(object):

    __start_node_label = '<fst_start>'
    __final_node_label = '<fst_final>'
    __sli = '<sil>'
    __eps = '<eps>'
    __filler = 'filler'
    __filer_edge_value = 2.0
    __eps_edge_value = -1.0

    def __init__(self):

        self.__rules = []
        self.__hash = {}
        self.__index = 0
        self.__textMap = {}
        self.__graph_nodes = []

    def load_dict(self, file_path):

        with open(file_path, "r", encoding="utf-8") as f:
            json_obj = json.load(f)

        for item in json_obj:
            rule = Rule(item['name'], item['units'], item['expressions'], item['isStartRule'])
            self.__rules.append(rule)

        return self.__rules

    def __add_node(self, uuid_in, terminal=False):

        self.__hash[uuid_in] = self.__index
        node = GraphNode(uuid_in)
        self.__graph_nodes.append(node)
        self.__index += 1
        if terminal:
            mirror_node = GraphNode(uuid.uuid1())
            self.__graph_nodes.append(mirror_node)
            p, q = self.__index - 1, self.__index
            edge = FstEdge(q, 1.0, self.__get_text(uuid_in))
            self.__graph_nodes[p].make_edge(edge)
            self.__index += 1
        return self.__index-1

    def __add_filler_cycle(self, id_in_graph):

        p = id_in_graph
        node0 = GraphNode(uuid.uuid1())
        self.__graph_nodes.append(node0)
        node0_id = self.__index
        self.__index += 1
        self.__graph_nodes[p].make_edge(FstEdge(node0_id, self.__eps_edge_value, self.__eps))

        node1 = GraphNode(uuid.uuid1())
        self.__graph_nodes.append(node1)
        node1_id = self.__index
        self.__index += 1
        self.__graph_nodes[node0_id].make_edge(FstEdge(node1_id, self.__filer_edge_value, self.__filler))
        self.__graph_nodes[node1_id].make_edge(FstEdge(p, self.__eps_edge_value, self.__eps))

    def __get_text(self, uuid_in):

        if uuid_in in self.__textMap:
            return self.__textMap[uuid_in]

        return ""

    def __get_index(self, uuid_in):

        if uuid_in in self.__hash:
            return self.__hash[uuid_in]
        print(uuid_in)
        return -1

    def __mk_nodes(self):

        # connect start node to terminal node
        start_id = self.__add_node(self.__start_node_label)
        final_id = self.__add_node(self.__final_node_label)
        self.__graph_nodes[start_id].make_edge(FstEdge(final_id, self.__eps_edge_value, self.__eps))

        # connect start node to filler
        self.__add_filler_cycle(0)

        for rule in self.__rules:
            rule.idInGraph = self.__add_node(rule.name)
            self.__add_filler_cycle(rule.idInGraph)

        for rule in self.__rules:
            for unit in rule.units:
                if not unit.terminal:
                    continue
                self.__textMap[unit.uuid] = unit.name
                unit.idInGraph = self.__add_node(unit.uuid, unit.terminal)

    def print(self):

        for i in range(0, len(self.__graph_nodes)):

            if i == self.__get_index(self.__final_node_label):
                print(i)
                continue

            graph_node = self.__graph_nodes[i]
            for (key, edge) in graph_node.edges.items():
                if edge.score >= 0:
                    print("%d %d %s %s %.2f" % (i, edge.dst, edge.text, edge.text, edge.score))
                else:
                    print("%d %d %s %s" % (i, edge.dst, edge.text, edge.text))

    def mk_graph(self):

        self.__mk_nodes()
        start_node_id = self.__get_index(self.__start_node_label)
        for rule in self.__rules:
            rule_id = rule.idInGraph
            # print(rule_id)

            for unit in rule.units:
                if unit.terminal:
                    unit_id = self.__get_index(unit.uuid)
                    self.__graph_nodes[rule_id].make_edge(FstEdge(unit_id, self.__eps_edge_value, self.__eps))
                    mirror_id = unit_id + 1
                    self.__graph_nodes[mirror_id].make_edge(FstEdge(rule_id, self.__eps_edge_value, self.__eps))

            for expression in rule.expressions:
                current_id = rule_id
                print("rule_id: %d" % current_id)
                if not expression[0].terminal:
                    current_id = self.__get_index(expression[0].name)
                if rule.isStartRule:
                    edge = FstEdge(current_id, self.__eps_edge_value, self.__eps)
                    self.__graph_nodes[start_node_id].make_edge(edge)
                    print(" %d -> %d " % (start_node_id, current_id))

                for i in range(1, len(expression)):
                    unit = expression[i]
                    dst_id = rule_id
                    if not unit.terminal:
                        dst_id = self.__get_index(unit.name)
                    self.__graph_nodes[current_id].make_edge(FstEdge(dst_id, self.__eps_edge_value, self.__eps))
                    print(" %d -> %d " % (current_id, dst_id))
                    current_id = dst_id
                dst_id = self.__get_index(self.__final_node_label)
                print(" %d -> %d " % (current_id, dst_id))
                self.__graph_nodes[current_id].make_edge(FstEdge(dst_id, self.__eps_edge_value, self.__eps))
